home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / imf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  29.9 KB  |  1,220 lines  |  [TEXT/R*ch]

  1. /* Interpretation of generic GDL images for Xconq.
  2.    Copyright (C) 1994, 1995, 1996 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* Note!  This file does not use the standard "conq.h" header, so can't assume
  10.    all the usual definitions. */
  11.  
  12. #include "config.h"
  13. #include "misc.h"
  14. #include "lisp.h"
  15. #include "imf.h"
  16.  
  17. /* RGB above this value should be considered white. */
  18.  
  19. #define WHITE_THRESHOLD (65535 - 100)
  20.  
  21. /* RGB below this value should be considered black. */
  22.  
  23. #define BLACK_THRESHOLD (    0 + 100)
  24.  
  25. /* (should remove these fixed limits someday) */
  26.  
  27. #define MAXIMAGEFAMILIES 1000
  28.  
  29. #define MAXIMAGEPALETTES 100
  30.  
  31. #define MAXIMAGECOLORS 1000
  32.  
  33. enum {
  34.     K_MONO_,
  35.     K_MASK_,
  36.     K_COLR_,
  37.     K_OTHER_
  38. };
  39.  
  40. static ImageFamily *new_imf PARAMS ((char *name));
  41. static int colornamecmp PARAMS ((char *str1, char *str2));
  42. static int bitmaps_match PARAMS ((int w, int h, Obj *lispdata, char *rawdata));
  43. static int color_matches_mono PARAMS ((Image *img));
  44. static void write_pixmap PARAMS ((FILE *fp, int w, int h, int aw, int ah,
  45.                  int pixelsize, int rowbytes,
  46.                  Obj *palette, int *rawpalette, int numcolors,
  47.                  Obj *lispdata, char *rawdata));
  48. static void write_bitmap PARAMS ((FILE *fp, char *subtyp, int w, int h,
  49.                  Obj *data, char *rawdata));
  50. static void write_palette_contents PARAMS ((FILE *fp, Obj *palette,
  51.                         int *rawpalette, int numcolors));
  52.  
  53. /* This is the array and count of known image families. */
  54.  
  55. ImageFamily **images;
  56.  
  57. int numimages = 0;
  58.  
  59. /* This is the array and count of named palettes. */
  60.  
  61. ImagePalette **palettes;
  62.  
  63. int numpalettes = 0;
  64.  
  65. /* This is the array and count of named colors. */
  66.  
  67. ImageColor **colors;
  68.  
  69. int numcolors = 0;
  70.  
  71. ImageFile *image_files;
  72.  
  73. /* Create and return an image family. */
  74.  
  75. static ImageFamily *
  76. new_imf(name)
  77. char *name;
  78. {
  79.     ImageFamily *imf;
  80.  
  81.     imf = (ImageFamily *) xmalloc(sizeof(ImageFamily));
  82.     imf->name = name;
  83.     imf->notes = lispnil;
  84.     return imf;
  85. }
  86.  
  87. ImageFamily *
  88. clone_imf(imf)
  89. ImageFamily *imf;
  90. {
  91.     Image *img, *img2, *truenext;
  92.     ImageFamily *imf2;
  93.  
  94.     imf2 = new_imf(imf->name);
  95.     memcpy(imf2, imf, sizeof(ImageFamily));
  96.     imf2->images = NULL;
  97.     /* Clone the images. */
  98.     for (img = imf->images; img != NULL; img = img->next) {
  99.     img2 = get_img(imf2, img->w, img->h);
  100.     truenext = img2->next;
  101.     memcpy(img2, img, sizeof(Image));
  102.     /* Clear the hook, we expect that the caller of this routine
  103.        will supply any new hook that might be necessary. */
  104.     img2->hook = NULL;
  105.     /* Restore the link. */
  106.     img2->next = truenext;
  107.     /* Note that pointers to raw image data and suchlike can be
  108.        left as-is, since they should be shared by image clones. */
  109.     }
  110.     return imf2;
  111. }
  112.  
  113. /* Test that the given name is a valid image family name (all alphanumeric,
  114.    hyphens anywhere but as first char). */
  115.  
  116. int
  117. valid_imf_name(name)
  118. char *name;
  119. {
  120.     char *tmp;
  121.  
  122.     for (tmp = name; *tmp; ++tmp) {
  123.     if (!(isalnum(*tmp)
  124.           || (tmp != name && *tmp == '-')))
  125.       return FALSE;
  126.     }
  127.     return TRUE;
  128. }
  129.  
  130. /* Given a name, find or create an image family with that name. */
  131.  
  132. ImageFamily *
  133. get_imf(name)
  134. char *name;
  135. {
  136.     ImageFamily *imf = NULL;
  137.     
  138.     if (name == NULL) {
  139.     init_warning("can't get an unnamed imf");
  140.     return NULL;
  141.     }
  142.     if (!valid_imf_name(name)) {
  143.     init_warning("\"%s\" is not a valid imf name", name);
  144.     return NULL;
  145.     }
  146.     if (images == NULL) {
  147.     images =
  148.       (ImageFamily **) xmalloc(MAXIMAGEFAMILIES * sizeof(ImageFamily *));
  149.     }
  150.     imf = find_imf(name);
  151.     if (imf == NULL) {
  152.     if (numimages >= MAXIMAGEFAMILIES) {
  153.         return NULL;
  154.     }
  155.     imf = new_imf(name);
  156.     if (imf != NULL) {
  157.         images[numimages++] = imf;
  158.     }
  159.     }
  160.     return imf;
  161. }
  162.  
  163. ImageFile *
  164. get_image_file(name)
  165. char *name;
  166. {
  167.     ImageFile *imfile;
  168.     
  169.     if (name == NULL)
  170.       run_error("can't get an unnamed imfile");
  171.     for (imfile = image_files; imfile != NULL; imfile = imfile->next) {
  172.     if (strcmp(name, imfile->name) == 0)
  173.       return imfile;
  174.     }
  175.     imfile = (ImageFile *) xmalloc(sizeof(ImageFile));
  176.     imfile->name = copy_string(name);
  177.     imfile->next = image_files;
  178.     image_files = imfile;
  179.     return imfile;
  180. }
  181.  
  182. void
  183. load_image_families(fp, loadnow, callback)
  184. FILE *fp;
  185. int loadnow;
  186. void (*callback) PARAMS ((ImageFamily *imf, int loadnow));
  187. {
  188.     int done = FALSE, rslt, first = TRUE;
  189.     char buf1[80], buf2[80];
  190.     ImageFamily *imf;
  191.     ImageFile *imfile;
  192.     
  193.     while (!done) {
  194.     rslt = fscanf(fp, "%s %s\n", buf1, buf2);
  195.     if (rslt != 2)
  196.       break;
  197.     else if (strcmp(buf1, ".") == 0
  198.          && strcmp(buf2, ".") == 0)
  199.       done = TRUE;
  200.     else if (first) {
  201.         if (strcmp(buf1, "ImageFamilyName") == 0
  202.         && strcmp(buf2, "FileName") == 0)
  203.           first = FALSE;
  204.         else {
  205.         init_warning("File not a valid imf dir, will close and ignore");
  206.         /* We've already given a warning message, so pretend we're done
  207.            so the format error message doesn't get displayed below. */
  208.         done = TRUE;
  209.         break;
  210.         }
  211.     } else {
  212.         imf = get_imf(copy_string(buf1));
  213.         if (imf != NULL) {
  214.         imfile = get_image_file(buf2);
  215.         imf->location = imfile;
  216.         if (loadnow && !imfile->loaded) {
  217.             load_imf_file(imfile->name, callback);
  218.             imfile->loaded = TRUE;
  219.         } else {
  220.             if (callback != NULL)
  221.               (*callback)(imf, loadnow);
  222.         }
  223.         }
  224.     }
  225.     }
  226.     if (!done) {
  227.     init_warning("Format error in imf dir near %s, will only use part",
  228.              (imf ? imf->name : "???"));
  229.     }
  230. }
  231.  
  232. /* Given a filename, open it and read/interpret all the image-related
  233.    forms therein. */
  234.  
  235. int
  236. load_imf_file(filename, callback)
  237. char *filename;
  238. void (*callback) PARAMS ((ImageFamily *imf, int loadnow));
  239. {
  240.     int startlineno = 1, endlineno = 1;
  241.     Obj *form;
  242.     FILE *fp;
  243.  
  244.     fp = fopen(filename, "r");
  245.     if (fp != NULL) {
  246.     /* Read everything in the file. */
  247.     while ((form = read_form(fp, &startlineno, &endlineno)) != lispeof) {
  248.         interp_imf_form(form, callback);
  249.     }
  250.     fclose(fp);
  251.     return TRUE;
  252.     }
  253.     return FALSE;
  254. }
  255.  
  256. /* Interpret a form, looking specifically for image-related forms. */
  257.  
  258. void
  259. interp_imf_form(form, imf_callback)
  260. Obj *form;
  261. void (*imf_callback) PARAMS ((ImageFamily *imf, int loadnow));
  262. {
  263.     Obj *head;
  264.     ImageFamily *imf;
  265.  
  266.     /* Ignore any non-lists, we might be reading from a normal game design. */
  267.     if (!consp(form))
  268.       return;
  269.     head = car(form);
  270.     if (match_keyword(head, K_IMF)) {
  271.     imf = interp_imf(form);
  272.     if (imf_callback != NULL && imf != NULL)
  273.       (*imf_callback)(imf, TRUE);
  274.     } else if (match_keyword(head, K_PALETTE)) {
  275.     interp_palette(form);
  276.     } else if (match_keyword(head, K_COLOR)) {
  277.     interp_color(form);
  278.     } else {
  279.     /* Ignore any non-image forms, we might be reading from a 
  280.        normal game design. */
  281.     }
  282. }
  283.  
  284. /* Find the image family of the given name, if it exists. */
  285.  
  286. ImageFamily *
  287. find_imf(name)
  288. char *name;
  289. {
  290.     int i;
  291.  
  292.     for (i = 0; i < numimages; ++i) {
  293.     if (strcmp(name, images[i]->name) == 0)
  294.       return images[i];
  295.     }
  296.     return NULL;
  297. }
  298.  
  299. /* Get an image of the given size from the family, creating a new one
  300.    if necessary. */
  301.  
  302. Image *
  303. get_img(imf, w, h)
  304. ImageFamily *imf;
  305. int w, h;
  306. {
  307.     Image *img, *previmg = NULL;
  308.  
  309.     for (img = imf->images; img != NULL; img = img->next) {
  310.     if (w == img->w && h == img->h)
  311.       return img;
  312.     previmg = img;
  313.     }
  314.     /* Not found; create a new image and add it to the family. */
  315.     img = (Image *) xmalloc(sizeof(Image));
  316.     img->w = w;  img->h = h;
  317.     /* Default min/max limits to actual size. */
  318.     img->minw = img->maxw = w;  img->minh = img->maxh = h;
  319.     img->embedx = img->embedy = -1;
  320.     img->embedw = img->embedh = -1;
  321.     img->monodata = img->colrdata = img->maskdata = lispnil;
  322.     img->palette = lispnil;
  323.     img->actualw = w;  img->actualh = h;
  324.     img->pixelsize = img->rowbytes = 0;
  325.     img->notes = lispnil;
  326.     /* Rely on zeroing of xmalloc blocks to avoid clearing other fields. */
  327.     /* Link at front of list of images. */
  328.     if (previmg != NULL)
  329.       previmg->next = img;
  330.     else
  331.       imf->images = img;
  332.     ++(imf->numsizes);
  333.     return img;
  334. }
  335.  
  336. Image *
  337. find_img(imf, w, h)
  338. ImageFamily *imf;
  339. int w, h;
  340. {
  341.     Image *img;
  342.     
  343.     for (img = imf->images; img != NULL; img = img->next) {
  344.     if (w == img->w && h == img->h)
  345.       return img;
  346.     }
  347.     return NULL;
  348. }
  349.  
  350. ImageFamily *
  351. interp_imf(form)
  352. Obj *form;
  353. {
  354.     ImageFamily *imf;
  355.  
  356.     if (stringp(cadr(form))) {
  357.     imf = get_imf(c_string(cadr(form)));
  358.     if (imf != NULL) {
  359.         interp_imf_contents(imf, cddr(form));
  360.     }
  361.     return imf;
  362.     } else {
  363.     /* garbage form */
  364.     }
  365.     return NULL;
  366. }
  367.  
  368. void
  369. interp_imf_contents(imf, clauses)
  370. ImageFamily *imf;
  371. Obj *clauses;
  372. {
  373.     Obj *rest, *clause;
  374.  
  375.     for (rest = clauses; rest != lispnil; rest = cdr(rest)) {
  376.     clause = car(rest);
  377.     if (consp(clause)) {
  378.         if (symbolp(car(clause))) {
  379.         if (match_keyword(car(clause), K_NOTES)) {
  380.             imf->notes = cadr(clause);
  381.             /* (should complain about non-nil cddr?) */
  382.         } else {
  383.             /* (should complain about syntax) */
  384.         }
  385.         } else {
  386.         interp_image(imf, car(clause), cdr(clause));
  387.         }
  388.     } else {
  389.         /* garbage? */
  390.     }
  391.     }
  392. }
  393.  
  394. void
  395. interp_image(imf, size, parts)
  396. ImageFamily *imf;
  397. Obj *size, *parts;
  398. {
  399.     int w, h, imtype, emx, emy;
  400.     char *name;
  401.     Image *img;
  402.     Obj *head, *rest, *typ, *prop, *proptype, *datalist;
  403.     
  404.     w = c_number(car(size));  h = c_number(cadr(size));
  405.     img = get_img(imf, w, h);
  406.     if (img == NULL)
  407.       run_error("no image?");
  408.     if (match_keyword(car(cddr(size)), K_TILE))
  409.       img->istile = TRUE;
  410.     for (rest = parts; rest != lispnil; rest = cdr(rest)) {
  411.     head = car(rest);
  412.     typ = car(head);
  413.     imtype = K_OTHER_;
  414.     if (match_keyword(typ, K_MONO)) {
  415.         imtype = K_MONO_;
  416.     } else if (match_keyword(typ, K_MASK)) {
  417.         imtype = K_MASK_;
  418.     } else if (match_keyword(typ, K_COLOR)) {
  419.         imtype = K_COLR_;
  420.     } else if (match_keyword(typ, K_EMBED)) {
  421.         name = c_string(cadr(head));
  422.         if (img->embedname != NULL
  423.         && strcmp(img->embedname, name) != 0)
  424.           run_warning("Changing embed name from \"%s\" to \"%s\" in %dx%d image of \"%s\"",
  425.               img->embedname, name, w, h, imf->name);
  426.         img->embedname = name;
  427.     } else if (match_keyword(typ, K_EMBED_AT)) {
  428.         emx = c_number(cadr(head));  emy = c_number(caddr(head));
  429.         if ((img->embedx >= 0 && emx != img->embedx)
  430.         || (img->embedy >= 0 && emy != img->embedy))
  431.           run_warning("Changing embed x,y from %d,%d to %d,%d in %dx%d image of \"%s\"",
  432.               img->embedx, img->embedy, emx, emy, w, h, imf->name);
  433.         img->embedx = emx;  img->embedy = emy;
  434.     } else if (match_keyword(typ, K_NOTES)) {
  435.         img->notes = cadr(head);
  436.         /* (should complain about non-nil cddr?) */
  437.     } else {
  438.         run_warning("unknown image property in \"%s\"", imf->name);
  439.     }
  440.     if (imtype == K_OTHER_)
  441.       continue;
  442.     datalist = cdr(head);
  443.     /* Interpret random image subproperties. */
  444.     while (consp(car(datalist))) {
  445.         prop = car(datalist);
  446.         proptype = car(prop);
  447.         if (match_keyword(proptype, K_ACTUAL)) {
  448.         img->actualw = c_number(cadr(prop));
  449.         img->actualh = c_number(caddr(prop));
  450.         } else if (match_keyword(proptype, K_PIXEL_SIZE)) {
  451.         img->pixelsize = c_number(cadr(prop));
  452.         } else if (match_keyword(proptype, K_ROW_BYTES)) {
  453.         img->rowbytes = c_number(cadr(prop));
  454.         } else if (match_keyword(proptype, K_PALETTE)) {
  455.         if (img->palette != lispnil && !equal(cdr(prop), img->palette))
  456.           run_warning("Changing palette in %dx%d image of \"%s\"",
  457.                   w, h, imf->name);
  458.         img->palette = cdr(prop);
  459.         } else {
  460.         run_warning("unknown image subproperty in \"%s\"", imf->name);
  461.         }
  462.         datalist = cdr(datalist);
  463.     }
  464.     switch (imtype) {
  465.       case K_MONO_:
  466.         if (img->monodata != lispnil && !equal(datalist, img->monodata))
  467.           run_warning("Changing mono data in %dx%d image of \"%s\"",
  468.               w, h, imf->name);
  469.         img->monodata = datalist;
  470.         break;
  471.       case K_COLR_:
  472.         if (img->colrdata != lispnil && !equal(datalist, img->colrdata))
  473.           run_warning("Changing color data in %dx%d image of \"%s\"",
  474.               w, h, imf->name);
  475.         img->colrdata = datalist;
  476.         break;
  477.       case K_MASK_:
  478.         if (img->maskdata != lispnil && !equal(datalist, img->maskdata))
  479.           run_warning("Changing mask data in %dx%d image of \"%s\"",
  480.               w, h, imf->name);
  481.         img->maskdata = datalist;
  482.         break;
  483.       default:
  484.         break;
  485.     }
  486.     }
  487.     /* Kind of a hack. */
  488.     img->minw = img->w / 4;  img->minh = img->h / 4;
  489.     img->maxw = img->w * 4;  img->maxh = img->h * 4;
  490. }
  491.  
  492. void
  493. interp_bytes(datalist, numbytes, destaddr, jump)
  494. Obj *datalist;
  495. int numbytes, jump;
  496. char *destaddr;
  497. {
  498.     int i, j = 0;
  499.     char *data = NULL;
  500.  
  501.     for (i = 0; i < numbytes; ++i) {
  502.     if (data == NULL || data[j] == '\0') {
  503.         if (!stringp(car(datalist)))
  504.           return; /* (should warn somehow?) */
  505.         data = c_string(car(datalist));
  506.         j = 0;
  507.         datalist = cdr(datalist);
  508.     }
  509.     /* Just skip over slashes, which are for readability only. */
  510.     if (data[j] == '/')
  511.       ++j;
  512.     destaddr[i] = hextoi(data[j]) * 16 + hextoi(data[j+1]);
  513.     if (jump == 1 || (jump > 0 && i % jump == 0)) {
  514.         i += jump;
  515.         /* Be neat, put a zero in the location we're jumping over. */
  516.         /* (doesn't work for jump > 1, but that never happens anymore?) */
  517.         destaddr[i] = 0;
  518.     }
  519.     j += 2;
  520.     }
  521. }
  522.  
  523. ImagePalette *
  524. interp_palette(form)
  525. Obj *form;
  526. {
  527.     Obj *elts;
  528.     ImagePalette *imp;
  529.  
  530.     if (stringp(cadr(form))) {
  531.     imp = get_imp(c_string(cadr(form)));
  532.     elts = cddr(form);
  533.     if (consp(car(elts))
  534.         && symbolp(car(car(elts)))) {
  535.         if (match_keyword(car(car(elts)), K_NOTES)) {
  536.         imp->notes = cadr(car(elts));
  537.         } else {
  538.         }
  539.         elts = cdr(elts);
  540.     }
  541.     return imp;
  542.     }
  543.     return NULL;
  544. }
  545.  
  546. ImagePalette *
  547. new_image_palette(name)
  548. char *name;
  549. {
  550.     ImagePalette *imp;
  551.  
  552.     imp = (ImagePalette *) xmalloc(sizeof(ImagePalette));
  553.     imp->name = name;
  554.     imp->notes = lispnil;
  555.     return imp;
  556. }
  557.  
  558. char *
  559. canonical_palette_name(str)
  560. char *str;
  561. {
  562.     return str;
  563. }
  564.  
  565. /* Given a name, find or create an image palette with that name. */
  566.  
  567. ImagePalette *
  568. get_imp(name)
  569. char *name;
  570. {
  571.     ImagePalette *imp = NULL;
  572.  
  573.     if (name == NULL)
  574.       return NULL;
  575.     if (palettes == NULL)
  576.       palettes =
  577.     (ImagePalette **) xmalloc(MAXIMAGEPALETTES * sizeof(ImagePalette *));
  578.     if ((imp = find_imp(name)) == NULL) {
  579.     if (numpalettes >= MAXIMAGEPALETTES)
  580.       return NULL;
  581.     imp = new_image_palette(canonical_palette_name(name));
  582.     if (imp != NULL) {
  583.         palettes[numpalettes++] = imp;
  584.     }
  585.     }
  586.     return imp;
  587. }
  588.  
  589. /* Find the image Palette of the given name, if it exists. */
  590.  
  591. ImagePalette *
  592. find_imp(name)
  593. char *name;
  594. {
  595.     int i;
  596.  
  597.     for (i = 0; i < numpalettes; ++i) {
  598.     if (strcmp(name, palettes[i]->name) == 0)
  599.       return palettes[i];
  600.     }
  601.     return NULL;
  602. }
  603.  
  604. ImageColor *
  605. interp_color(form)
  606. Obj *form;
  607. {
  608.     Obj *elts;
  609.     ImageColor *imc;
  610.  
  611.     if (stringp(cadr(form))) {
  612.     imc = get_imc(c_string(cadr(form)));
  613.     elts = cddr(form);
  614.     if (consp(car(elts))
  615.         && symbolp(car(car(elts)))) {
  616.         if (match_keyword(car(car(elts)), K_NOTES)) {
  617.         imc->notes = cadr(car(elts));
  618.         } else {
  619.         }
  620.         elts = cdr(elts);
  621.     }
  622.     imc->r = c_number(car(elts));
  623.     imc->g = c_number(car(cdr(elts)));
  624.     imc->b = c_number(car(cddr(elts)));
  625.     return imc;
  626.     }
  627.     return NULL;
  628. }
  629.  
  630. ImageColor *
  631. new_image_color(name)
  632. char *name;
  633. {
  634.     ImageColor *imc;
  635.  
  636.     imc = (ImageColor *) xmalloc(sizeof(ImageColor));
  637.     imc->name = name;
  638.     imc->notes = lispnil;
  639.     return imc;
  640. }
  641.  
  642. char *
  643. canonical_color_name(str)
  644. char *str;
  645. {
  646.     return str;
  647. }
  648.  
  649. /* Given a name, find or create an image color with that name. */
  650.  
  651. ImageColor *
  652. get_imc(name)
  653. char *name;
  654. {
  655.     ImageColor *imc = NULL;
  656.  
  657.     if (name == NULL)
  658.       return NULL;
  659.     if (colors == NULL)
  660.       colors =
  661.     (ImageColor **) xmalloc(MAXIMAGECOLORS * sizeof(ImageColor *));
  662.     if ((imc = find_imc(name)) == NULL) {
  663.     if (numcolors >= MAXIMAGECOLORS)
  664.       return NULL;
  665.     imc = new_image_color(canonical_color_name(name));
  666.     if (imc != NULL) {
  667.         imc->r = imc->g = imc->b = 0;
  668.         colors[numcolors++] = imc;
  669.     }
  670.     }
  671.     return imc;
  672. }
  673.  
  674. /* Find the image color of the given name, if it exists. */
  675.  
  676. ImageColor *
  677. find_imc(name)
  678. char *name;
  679. {
  680.     int i;
  681.  
  682.     for (i = 0; i < numcolors; ++i) {
  683.     if (colornamecmp(name, colors[i]->name) == 0)
  684.       return colors[i];
  685.     }
  686.     return NULL;
  687. }
  688.  
  689. /* X-style color names have several variants, but we only want one of them,
  690.    so this matches the variants with each other. */
  691.  
  692. static int
  693. colornamecmp(str1, str2)
  694. char *str1, *str2;
  695. {
  696.     while (*str1 != '\0' && *str2 != '\0') {
  697.     if (*str1 == *str2) {
  698.         ++str1;  ++str2;
  699.     } else if (isalpha(*str1) && isalpha(*str2)
  700.            && tolower(*str1) == tolower(*str2)) {
  701.         ++str1;  ++str2;
  702.     } else if (*str1 == 'a' && *str2 == 'e'
  703.            && *(str1+1) == 'y' && *(str2+1) == 'y') {
  704.         ++str1;  ++str2;
  705.     } else if (*str1 == 'e' && *str2 == 'a'
  706.            && *(str1+1) == 'y' && *(str2+1) == 'y') {
  707.         ++str1;  ++str2;
  708. #if 0
  709.     } else if (*str1 == ' ') {
  710.         ++str1;
  711.     } else if (*str2 == ' ') {
  712.         ++str2;
  713. #endif
  714.     } else {
  715.         return *str1 - *str2;
  716.     }
  717.     }
  718.     if (*str1 == '\0') {
  719.     if (*str2 == '\0') {
  720.         return 0;
  721.     } else {
  722.         return 1;
  723.     }
  724.     } else {
  725.     if (*str2 == '\0') {
  726.         return -1;
  727.     } else {
  728.         /* can never happen, but humor the compiler */
  729.         return 0;
  730.     }
  731.     }
  732. }
  733.  
  734.  
  735. /* Try to find the best of multiple images for the given bounding box. */
  736. /* Don't return anything that won't fit in min space. */
  737.  
  738. Image *
  739. best_image(imf, w, h)
  740. ImageFamily *imf;
  741. int w, h;
  742. {
  743.     Image *img, *best = NULL, *fallback = NULL;
  744.  
  745.     if (imf == NULL)
  746.       return NULL;
  747.     for (img = imf->images; img != NULL; img = img->next) {
  748.     /* Exact matches need no further searching. */
  749.     /* The istile test is a hack for the general problem here, which
  750.        is that a high-quality color image might look better, even after
  751.        scaling, than a b/w image that is exactly the right size. */
  752.     if (w == img->w && h == img->h && !img->istile) {
  753.         return img;
  754.     } else if (between(img->minw, w, img->maxw)
  755.            && between(img->minh, h, img->maxh)) {
  756.         /* Image is plausible - go for the largest one that will fit. */
  757.         if (!best || (img->w > best->w && img->h > best->h))
  758.           best = img;
  759.     } else if (w >= img->minw && h >= img->minh) {
  760.         /* Image is not really big enough, but keep as a fallback. */
  761.         /* Prefer the largest fallback possible. */
  762.         if (!fallback || (img->w > fallback->w && img->h > fallback->h))
  763.           fallback = img;
  764.     } else if (img->istile) {
  765.         /* Image is not really appropriate, but keep as a fallback. */
  766.         /* Prefer the largest fallback possible. */
  767.         if (!fallback || (img->w > fallback->w && img->h > fallback->h))
  768.           fallback = img;
  769.     }
  770.     }
  771.     return (best ? best : fallback);
  772. }
  773.  
  774. /* The comparison function for the image list just does "strcmp" order
  775.    and *requires* that all image families be named and named uniquely. */
  776.  
  777. static int
  778. image_name_compare(imf1, imf2)
  779. #ifdef THINK_C
  780. const
  781. #endif
  782. void *imf1, *imf2;
  783. {
  784.     return strcmp((*((ImageFamily **) imf1))->name,
  785.           (*((ImageFamily **) imf2))->name);
  786. }
  787.  
  788. void
  789. sort_all_images()
  790. {
  791.     qsort(&(images[0]), numimages, sizeof(ImageFamily *), image_name_compare);
  792. }
  793.  
  794. /* The comparison function for the palette list just does "strcmp" order
  795.    and *requires* that all image palettes be named and named uniquely. */
  796.  
  797. static int
  798. palette_name_compare(imp1, imp2)
  799. #ifdef THINK_C
  800. const
  801. #endif
  802. void *imp1, *imp2;
  803. {
  804.     return strcmp((*((ImagePalette **) imp1))->name,
  805.           (*((ImagePalette **) imp2))->name);
  806. }
  807.  
  808. void
  809. sort_all_palettes()
  810. {
  811.     qsort(&(palettes[0]), numpalettes, sizeof(ImagePalette *), palette_name_compare);
  812. }
  813.  
  814. /* The comparison function for the color list just does "strcmp" order
  815.    and *requires* that all image palettes be named and named uniquely. */
  816.  
  817. static int
  818. color_name_compare(imc1, imc2)
  819. #ifdef THINK_C
  820. const
  821. #endif
  822. void *imc1, *imc2;
  823. {
  824.     return strcmp((*((ImageColor **) imc1))->name,
  825.           (*((ImageColor **) imc2))->name);
  826. }
  827.  
  828. void
  829. sort_all_colors()
  830. {
  831.     qsort(&(colors[0]), numcolors, sizeof(ImageColor *),  color_name_compare);
  832. }
  833.  
  834. /* Check Lisp-format and binary-format data for consistency. */
  835.  
  836. void
  837. check_imf(imf)
  838. ImageFamily *imf;
  839. {
  840.     Image *img;
  841.     
  842.     if (imf == NULL)
  843.       return;
  844.     if (imf->name == NULL) {
  845.     return;
  846.     }
  847.     for (img = imf->images; img != NULL; img = img->next) {
  848.     /* Check consistency of Lisp and binary data. */
  849.     if (img->colrdata != lispnil && img->rawcolrdata) {
  850.         /* (should add color image comparison) */
  851.     }
  852.     if (img->monodata != lispnil && img->rawmonodata) {
  853.         if (!bitmaps_match(img->w, img->h, img->monodata, img->rawmonodata))
  854.           run_warning("mono bitmap data not consistent in  %dx%d image of \"%s\"",
  855.               img->w, img->h, imf->name);
  856.     }
  857.     if (img->maskdata != lispnil && img->rawmaskdata) {
  858.         if (!bitmaps_match(img->w, img->h, img->maskdata, img->rawmaskdata))
  859.           run_warning("mask bitmap data not consistent in  %dx%d image of \"%s\"",
  860.               img->w, img->h, imf->name);
  861.     }
  862.     }
  863. }
  864.  
  865. static int
  866. bitmaps_match(w, h, lispdata, rawdata)
  867. int w, h;
  868. Obj *lispdata;
  869. char *rawdata;
  870. {
  871.     int i, j = 0, rowbytes, numbytes, byte, jump = 0;
  872.     char *datastr = NULL;
  873.  
  874.     rowbytes = (w + 7) / 8;
  875.     numbytes =  h * rowbytes;
  876.     for (i = 0; i < numbytes; ++i) {
  877.     if (datastr == NULL || datastr[j] == '\0') {
  878.         if (!stringp(car(lispdata)))
  879.           break;
  880.         datastr = c_string(car(lispdata));
  881.         j = 0;
  882.         lispdata = cdr(lispdata);
  883.     }
  884.     if (datastr[j] == '/')
  885.       ++j;
  886.     byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
  887.     j += 2;
  888.     if (byte != rawdata[i])
  889.       return FALSE;
  890.     }
  891.     return TRUE;
  892. }
  893.  
  894. /* Write out the entire image family. */
  895.  
  896. void
  897. write_imf(fp, imf)
  898. FILE *fp;
  899. ImageFamily *imf;
  900. {
  901.     Image *img;
  902.     
  903.     if (imf == NULL)
  904.       return;
  905.     if (imf->name == NULL) {
  906.     fprintf(fp, "; garbage image family?\n");
  907.     return;
  908.     }
  909.     if (imf->notes != lispnil) {
  910.     fprintf(fp, "(%s \"%s\"", keyword_name(K_IMF), imf->name);
  911.     fprintf(fp, "\n  (%s ", keyword_name(K_NOTES));
  912.     fprintlisp(fp, imf->notes);
  913.     fprintf(fp, "))\n");
  914.     }
  915.     for (img = imf->images; img != NULL; img = img->next) {
  916.     if (img->monodata != lispnil
  917.         || img->maskdata != lispnil
  918.         || img->colrdata != lispnil
  919.         || img->rawmonodata != NULL
  920.         || img->rawmaskdata != NULL
  921.         || img->rawcolrdata != NULL) {
  922.         fprintf(fp, "(%s \"%s\"", keyword_name(K_IMF), imf->name);
  923.         fprintf(fp, " (");
  924.         fprintf(fp, "(%d %d", img->w, img->h);
  925.         if (img->istile)
  926.           fprintf(fp, " %s", keyword_name(K_TILE));
  927.         fprintf(fp, ")");
  928.         if (img->embedname) {
  929.         fprintf(fp, " (%s \"%s\")",
  930.             keyword_name(K_EMBED), img->embedname);
  931.         }
  932.         if (img->embedx >= 0 && img->embedy >= 0) {
  933.         fprintf(fp, " (%s %d %d)",
  934.             keyword_name(K_EMBED_AT), img->embedx, img->embedy);
  935.         }
  936.         if (img->embedw >= 0 && img->embedh >= 0) {
  937.         fprintf(fp, " (%s %d %d)",
  938.             keyword_name(K_EMBED_SIZE), img->embedw, img->embedh);
  939.         }
  940.         if (img->notes != lispnil) {
  941.         fprintf(fp, "\n  (%s ", keyword_name(K_NOTES));
  942.         fprintlisp(fp, img->notes);
  943.         fprintf(fp, ")\n ");
  944.         }
  945.         if ((img->colrdata != lispnil || img->rawcolrdata)
  946.         && !color_matches_mono(img)) {
  947.         fprintf(fp, "\n  ");
  948.         write_pixmap(fp, img->w, img->h, img->actualw, img->actualh,
  949.                  img->pixelsize, img->rowbytes,
  950.                  img->palette, img->rawpalette, img->numcolors,
  951.                  img->colrdata, img->rawcolrdata);
  952.         }
  953.         if (img->monodata != lispnil || img->rawmonodata) {
  954.         fprintf(fp, "\n  ");
  955.         write_bitmap(fp, keyword_name(K_MONO), img->w, img->h,
  956.                  img->monodata, img->rawmonodata);
  957.         }
  958.         if (img->maskdata != lispnil || img->rawmaskdata) {
  959.         fprintf(fp, "\n  ");
  960.         write_bitmap(fp, keyword_name(K_MASK), img->w, img->h,
  961.                  img->maskdata, img->rawmaskdata);
  962.         }
  963.         fprintf(fp, "))\n");
  964.     }
  965.     }
  966. }
  967.  
  968. static int
  969. color_matches_mono(img)
  970. Image *img;
  971. {
  972.     int i, cj, mj, rowbytes, numbytes, cbyte, mbyte, jump = 0;
  973.     char *cdatastr = NULL, *mdatastr = NULL;
  974.     Obj *clispdata = img->colrdata, *mlispdata = img->monodata;
  975.  
  976.     if (img->pixelsize != 1)
  977.       return FALSE;
  978.     /* No match possible if not a black-white-only palette. */
  979.     if (!((img->numcolors == 2
  980.        && img->rawpalette != NULL
  981.        && img->rawpalette[0+0] == 0
  982.        && img->rawpalette[0+1] > WHITE_THRESHOLD
  983.        && img->rawpalette[0+2] > WHITE_THRESHOLD
  984.        && img->rawpalette[0+3] > WHITE_THRESHOLD
  985.        && img->rawpalette[4+0] == 1
  986.        && img->rawpalette[4+1] < BLACK_THRESHOLD
  987.        && img->rawpalette[4+2] < BLACK_THRESHOLD
  988.        && img->rawpalette[4+3] < BLACK_THRESHOLD)
  989.       || (img->palette != lispnil
  990.           && c_number(car(car(img->palette))) == 0
  991.           && c_number(cadr(car(img->palette))) > WHITE_THRESHOLD
  992.           && c_number(caddr(car(img->palette))) > WHITE_THRESHOLD
  993.           && c_number(caddr(cdr(car(img->palette)))) > WHITE_THRESHOLD
  994.           && c_number(car(cadr(img->palette))) == 1
  995.           && c_number(cadr(cadr(img->palette))) < BLACK_THRESHOLD
  996.           && c_number(caddr(cadr(img->palette))) < BLACK_THRESHOLD
  997.           && c_number(caddr(cdr(cadr(img->palette)))) < BLACK_THRESHOLD)))
  998.       return FALSE;
  999.     /* Now compare the contents. */
  1000.     rowbytes = (img->w + 7) / 8;
  1001.     numbytes =  img->h * rowbytes;
  1002.     cj = mj = 0;
  1003.     for (i = 0; i < numbytes; ++i) {
  1004.       if (clispdata != lispnil) {
  1005.     if (cdatastr == NULL || cdatastr[cj] == '\0') {
  1006.         if (!stringp(car(clispdata)))
  1007.           break;
  1008.         cdatastr = c_string(car(clispdata));
  1009.         cj = 0;
  1010.         clispdata = cdr(clispdata);
  1011.     }
  1012.     if (cdatastr[cj] == '/')
  1013.       ++cj;
  1014.     cbyte = hextoi(cdatastr[cj]) * 16 + hextoi(cdatastr[cj+1]);
  1015.     cj += 2;
  1016.       } else if (img->rawcolrdata != NULL) {
  1017.     cbyte = (img->rawcolrdata)[i];
  1018.       } else {
  1019.     return FALSE;
  1020.       }
  1021.       if (mlispdata != lispnil) {
  1022.     if (mdatastr == NULL || mdatastr[mj] == '\0') {
  1023.         if (!stringp(car(mlispdata)))
  1024.           break;
  1025.         mdatastr = c_string(car(mlispdata));
  1026.         mj = 0;
  1027.         mlispdata = cdr(mlispdata);
  1028.     }
  1029.     if (mdatastr[mj] == '/')
  1030.       ++mj;
  1031.     mbyte = hextoi(mdatastr[mj]) * 16 + hextoi(mdatastr[mj+1]);
  1032.     mj += 2;
  1033.       } else if (img->rawmonodata != NULL) {
  1034.     mbyte = (img->rawmonodata)[i];
  1035.       } else {
  1036.     return FALSE;
  1037.       }
  1038.       if (cbyte != mbyte)
  1039.     return FALSE;
  1040.     }
  1041.     return TRUE;
  1042. }
  1043.  
  1044. static void
  1045. write_pixmap(fp, w, h, actualw, actualh, pixelsize, rowbytes,
  1046.          palette, rawpalette, numcolors, lispdata, rawdata)
  1047. FILE *fp;
  1048. int w, h, actualw, actualh, pixelsize, rowbytes, *rawpalette, numcolors;
  1049. Obj *palette, *lispdata;
  1050. char *rawdata;
  1051. {
  1052.     int dolisp, i, j = 0, numbytes, byte, jump = 0;
  1053.     char *datastr = NULL;
  1054.  
  1055.     actualw = (actualw != 0 ? actualw : w);
  1056.     actualh = (actualh != 0 ? actualh : h);
  1057.     dolisp = (lispdata != lispnil);    
  1058.     numbytes = actualh * rowbytes;
  1059.     fprintf(fp, "(%s", keyword_name(K_COLOR));
  1060.     if (actualw != w || actualh != h)
  1061.       fprintf(fp, " (%s %d %d)", keyword_name(K_ACTUAL), actualw, actualh);
  1062.     fprintf(fp, " (%s %d)", keyword_name(K_PIXEL_SIZE), pixelsize);
  1063.     fprintf(fp, " (%s %d)", keyword_name(K_ROW_BYTES), rowbytes);
  1064.     if (palette != lispnil || (rawpalette && numcolors))
  1065.       write_palette_contents(fp, palette, rawpalette, numcolors);
  1066.     fprintf(fp, "\n   \"");
  1067.     for (i = 0; i < numbytes; ++i) {
  1068.     if (i > 0 && i % 32 == 0)
  1069.       fprintf(fp, "\"\n   \"");
  1070.     if (i > 0 && i % 32 != 0 && i % rowbytes == 0)
  1071.       fprintf(fp, "/");
  1072.     if (dolisp) {
  1073.         if (datastr == NULL || datastr[j] == '\0') {
  1074.         if (!stringp(car(lispdata)))
  1075.           break;
  1076.         datastr = c_string(car(lispdata));
  1077.         j = 0;
  1078.         lispdata = cdr(lispdata);
  1079.         }
  1080.         if (datastr[j] == '/')
  1081.           ++j;
  1082.         byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
  1083.         if (jump == 1 || (jump > 0 && i % jump == 0))
  1084.           i += jump;
  1085.         j += 2;
  1086.     } else {
  1087.         byte = rawdata[i];
  1088.     }
  1089.     fprintf(fp, "%02x", (unsigned char) byte);
  1090.     }
  1091.     fprintf(fp, "\")");
  1092. }
  1093.  
  1094. static void
  1095. write_bitmap(fp, subtyp, w, h, lispdata, rawdata)
  1096. FILE *fp;
  1097. char *subtyp;
  1098. int w, h;
  1099. Obj *lispdata;
  1100. char *rawdata;
  1101. {
  1102.     int dolisp, i, j = 0, rowbytes, numbytes, byte, jump = 0;
  1103.     char *datastr = NULL;
  1104.  
  1105.     /* Lisp data overrides byte data. */    
  1106.     dolisp = (lispdata != lispnil);    
  1107.     rowbytes = (w + 7) / 8;
  1108.     numbytes =  h * rowbytes;
  1109.     fprintf(fp, "(%s", subtyp);
  1110.     if (w > 16 || h > 16)
  1111.       fprintf(fp, "\n  ");
  1112.     fprintf(fp, " \"");
  1113.     for (i = 0; i < numbytes; ++i) {
  1114.     if (i > 0 && i % 32 == 0)
  1115.       fprintf(fp, "\"\n   \"");
  1116.     if (i > 0 && i % 32 != 0 && i % rowbytes == 0)
  1117.       fprintf(fp, "/");
  1118.     if (dolisp) {
  1119.         if (datastr == NULL || datastr[j] == '\0') {
  1120.         if (!stringp(car(lispdata)))
  1121.           break;
  1122.         datastr = c_string(car(lispdata));
  1123.         j = 0;
  1124.         lispdata = cdr(lispdata);
  1125.         }
  1126.         if (datastr[j] == '/')
  1127.           ++j;
  1128.         byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
  1129.         if (jump == 1 || (jump > 0 && i % jump == 0))
  1130.           i += jump;
  1131.         j += 2;
  1132.     } else {
  1133.         byte = rawdata[i];
  1134.     }
  1135.     fprintf(fp, "%02x", (unsigned char) byte);
  1136.     }
  1137.     fprintf(fp, "\")");
  1138. }
  1139.  
  1140. static void
  1141. write_palette_contents(fp, palette, rawpalette, numcolors)
  1142. FILE *fp;
  1143. Obj *palette;
  1144. int *rawpalette, numcolors;
  1145. {
  1146.     int len, i;
  1147.     Obj *color;
  1148.  
  1149.     len = (palette != lispnil ? length(palette) : numcolors);
  1150.     if (len > 2)
  1151.       fprintf(fp, "\n  ");
  1152.     fprintf(fp, " (%s", keyword_name(K_PALETTE));
  1153.     if (palette != lispnil) {
  1154.     for (; palette != lispnil; palette = cdr(palette)) {
  1155.         color = car(palette);
  1156.         if (len > 2)
  1157.           fprintf(fp, "\n   ");
  1158.         fprintf(fp, " (%d %d %d %d)",
  1159.             c_number(car(color)),
  1160.             c_number(car(cdr(color))),
  1161.             c_number(car(cdr(cdr(color)))),
  1162.             c_number(car(cdr(cdr(cdr(color))))));
  1163.     }
  1164.     } else {
  1165.      for (i = 0; i < numcolors; i++) {
  1166.         if (len > 2)
  1167.           fprintf(fp, "\n   ");
  1168.          fprintf(fp, " (%d %d %d %d)",
  1169.              rawpalette[4*i],
  1170.              rawpalette[4*i+1],
  1171.              rawpalette[4*i+2],
  1172.              rawpalette[4*i+3]);
  1173.      }
  1174.     }
  1175.     fprintf(fp, ")");
  1176. }
  1177.  
  1178. void
  1179. write_imp(fp, imp)
  1180. FILE *fp;
  1181. ImagePalette *imp;
  1182. {
  1183.     if (imp == NULL)
  1184.       return;
  1185.     if (imp->name == NULL) {
  1186.     fprintf(fp, "; garbage palette?\n");
  1187.     return;
  1188.     }
  1189.     fprintf(fp, "(%s \"%s\"",
  1190.         keyword_name(K_PALETTE), imp->name);
  1191.     if (imp->notes != lispnil) {
  1192.     fprintf(fp, "\n  (%s ", keyword_name(K_NOTES));
  1193.     fprintlisp(fp, imp->notes);
  1194.     fprintf(fp, ")\n ");
  1195.     }
  1196.     /* (should dump out palette entries here) */
  1197.     fprintf(fp, ")\n");
  1198. }
  1199.  
  1200. void
  1201. write_imc(fp, imc)
  1202. FILE *fp;
  1203. ImageColor *imc;
  1204. {
  1205.     if (imc == NULL)
  1206.       return;
  1207.     if (imc->name == NULL) {
  1208.     fprintf(fp, "; garbage color?\n");
  1209.     return;
  1210.     }
  1211.     fprintf(fp, "(%s \"%s\"",
  1212.         keyword_name(K_COLOR), imc->name);
  1213.     if (imc->notes != lispnil) {
  1214.     fprintf(fp, "\n  (%s ", keyword_name(K_NOTES));
  1215.     fprintlisp(fp, imc->notes);
  1216.     fprintf(fp, ")\n ");
  1217.     }
  1218.     fprintf(fp, " %d %d %d)\n", imc->r, imc->g, imc->b);
  1219. }
  1220.